#include "Player.h"
#include "Ground.h"
#include <QRandomGenerator>

const float Player::maxGlideEnergy = 300.0f;
const float Player::glideEnergyConsumption = 0.85f;

Player::Player(int windowWidth, int lifeCount)
    : faceSide(1),
    lifeCount(lifeCount),
    x(580), y(700),
    windowWidth(windowWidth),
    gravity(0.6f),
    velocityX(0.0f),
    velocityY(0.0f),
    isJumping(false),
    groundY(700),
    isGliding(false),
    invincible(false),
    invincibleEndTime(0),
    checkKeyPressed(false),
    glideEnergy(maxGlideEnergy),
    bulletCount(0) {
    playerImage1.load(":/player1.png");
    playerImage0.load(":/player2.png");
    glideImage.load(":/glide0.png");
    for (int i = 0; i < MAX_BULLETS; ++i) {
        bullets[i] = nullptr;
    }
}

Player::~Player() {
    for (int i = 0; i < MAX_BULLETS; ++i) {
        delete bullets[i];
    }
}

void Player::activateInvincibility() {
    invincible = true;
    invincibleEndTime = QDateTime::currentMSecsSinceEpoch() + 4000;
}

bool Player::isInvincible() const {
    return invincible && (QDateTime::currentMSecsSinceEpoch() < invincibleEndTime);
}

void Player::startGlide() {
    if (isJumping && glideEnergy > 0) {
        isGliding = true;
    }
}

void Player::stopGlide() {
    isGliding = false;
}

void Player::draw(QPainter *painter) {
    if (isInvincible()) {
        painter->setOpacity(0.5);
    }
    faceSideChange();
    if (isGliding) {
        painter->drawImage(QRect(x, y, width, height), !faceSide ? glideImage : glideImage.mirrored(true, false));
    } else {
        painter->drawImage(QRect(x, y, width, height), faceSide ? playerImage1 : playerImage0);
    }
    painter->setOpacity(1.0);
    painter->setBrush(Qt::red);
    painter->drawRect(x + 23, y + 30, realWidth, realHeight);

    for (int i = 0; i < MAX_BULLETS; ++i) {
        if (bullets[i]) {
            painter->drawImage(bullets[i]->rect(), bullets[i]->ball);
        }
    }
}

void Player::faceSideChange() {
    if (faceSide == 0) {
        if (velocityX > 0) faceSide = 1;
    }
    if (faceSide == 1) {
        if (velocityX < 0) faceSide = 0;
    }
}

void Player::move(int dx) {
    velocityX = dx;
}

void Player::jump() {
    if (!isJumping && !isGliding) {
        velocityY = -14.0f;
        isJumping = true;
    } else {
        if (!doubleJumping) {
            velocityY = -14.0f;
            doubleJumping = true;
        }
    }
}

void Player::scoreSkillAt_Z(int& score) {
    if (score > 200) {
        score -= 200;
        activateInvincibility();
    }
}

void Player::scoreSkillAt_X(int& score, QList<Obstacle*>& obstacles) {
    if (score > 300) {
        score -= 300;
        for (Obstacle* obs : obstacles) {
            obs->setY(QRandomGenerator::global()->bounded(-2000, -500));
            obs->setX(QRandomGenerator::global()->bounded(800));
        }
    }
}

void Player::lifeDown() {
    lifeCount--;
}

int Player::getLife() {
    return lifeCount;
}

int Player::getEnergy() {
    return glideEnergy;
}

void Player::addGlideEnergy() {
    glideEnergy += 80.0f;
    if (glideEnergy > maxGlideEnergy) glideEnergy = maxGlideEnergy;
}

void Player::update(const Ground *ground) {
    int newX = x + velocityX;
    int checkX = (velocityX > 0) ? x + width : x;
    int groundHeight = ground->getHeightAt(checkX);
    int currentHeight = ground->getHeightAt(x + width / 2);
    int heightDiff = currentHeight - groundHeight;

    if (isJumping) {
        int playerBottom = y + height;
        if (playerBottom < groundHeight) {
            x = newX;
        } else {
            if (heightDiff * 2 > height && velocityX != 0) {
                velocityX = 0.0f;
            }
        }
    } else {
        if (heightDiff * 2 > height && velocityX != 0) {
            newX = x;
            velocityX = 0.0f;
        } else {
            x = newX;
        }
    }

    if (x < 0) x = 0;
    if (x + width > windowWidth) x = windowWidth - width;

    if (!isJumping || velocityX != 0) {
        if (velocityX > 0) (isGliding) ? velocityX -= 0.1f : velocityX -= 0.3f;
        else if (velocityX < 0) (isGliding) ? velocityX += 0.1f : velocityX += 0.3f;
        if (std::abs(velocityX) < 0.3f) velocityX = 0.0f;
    }

    y += velocityY;
    if (isGliding && glideEnergy > 0) {
        if (velocityY > -5) velocityY += gravity * 1 / 7;
        else if (velocityY < 5) {
            velocityY += gravity * 3 / 2;
        }
        glideEnergy -= glideEnergyConsumption;
        if (glideEnergy < 0) {
            glideEnergy = 0;
            stopGlide();
        }
    } else {
        velocityY += (abs(velocityY) <= 4) ? gravity * 3 / 4 : gravity;
    }

    if (y + height >= currentHeight) {
        y = currentHeight - height;
        velocityY = 0.0f;
        isJumping = false;
        doubleJumping = false;
        isGliding = false;
    }
}

QRect Player::rect() const {
    return QRect(x + 23, y + 30, realWidth, realHeight);
}

QRect Player::rectWithRewardBlock() const {
    return QRect(x, y, width, height);
}

void Player::shoot(int targetX, int targetY) {
    if (bulletCount < MAX_BULLETS&&glideEnergy>12) {
        int startX = x + width / 2;
        int startY = y + height / 2;
        for (int i = 0; i < MAX_BULLETS; ++i) {
            if (!bullets[i]) {
                bullets[i] = new Bullet(startX, startY, targetX, targetY);
                ++bulletCount;
                break;
            }
        }
        glideEnergy-=5;
    }
}

void Player::updateBullets(const QList<Obstacle*>& obstacles, const QList<HorizontalObstacle*>& horiObstacles) {
    for (int i = 0; i < MAX_BULLETS; ++i) {
        if (bullets[i]) {
            bullets[i]->move(bullets[i]->sx,bullets[i]->sy);

            // 边界检测
            if (bullets[i]->x < 0 || bullets[i]->x > windowWidth || bullets[i]->y < 0 || bullets[i]->y > 800) {
                delete bullets[i];
                bullets[i] = nullptr;
                --bulletCount;
                continue;
            }

            // 与普通障碍物的碰撞检测
            for (int j = 0; j < obstacles.size(); ++j) {
                if (bullets[i]->rect().intersects(obstacles[j]->rect())) {
                    delete bullets[i];
                    bullets[i] = nullptr;
                    --bulletCount;
                    delete obstacles[j];
                    const_cast<QList<Obstacle*>&>(obstacles).removeAt(j);
                    break;
                }
            }

            if (bullets[i]) {
                // 与横向障碍物的碰撞检测
                for (int k = 0; k < horiObstacles.size(); ++k) {
                    if (bullets[i]->rect().intersects(horiObstacles[k]->rect())) {
                        delete bullets[i];
                        bullets[i] = nullptr;
                        --bulletCount;
                        delete horiObstacles[k];
                        const_cast<QList<HorizontalObstacle*>&>(horiObstacles).removeAt(k);
                        break;
                    }
                }
            }
        }
    }
}
